﻿namespace Pyramid.Hardware.Gates
{
	using System;

	/// <summary>
	/// Represents a logic gate that accepts two input signals.
	/// </summary>
	public abstract class BinaryGate
		: Gate
	{
		private ILine _inputLineA;
		private ILine _inputLineB;
		private Func<Trivalent, Trivalent, Trivalent> _operation;

		protected BinaryGate(ILine inputLineA, ILine inputLineB, Func<Trivalent, Trivalent, Trivalent> operation)
		{
			_operation = operation;
			InputLineA = inputLineA;
			InputLineB = inputLineB;
		}

		public ILine InputLineA
		{
			get
			{
				return _inputLineA;
			}
			private set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				if (_inputLineA != null)
					_inputLineA.SignalChange -= new EventHandler<SignalChangeEventArgs>(_inputLine_SignalChange);

				_inputLineA = value;

				_inputLineA.SignalChange += new EventHandler<SignalChangeEventArgs>(_inputLine_SignalChange);

				UpdateSignal();
			}
		}

		public ILine InputLineB
		{
			get
			{
				return _inputLineB;
			}
			private set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				if (_inputLineB != null)
					_inputLineB.SignalChange -= new EventHandler<SignalChangeEventArgs>(_inputLine_SignalChange);

				_inputLineB = value;

				_inputLineB.SignalChange += new EventHandler<SignalChangeEventArgs>(_inputLine_SignalChange);

				UpdateSignal();
			}
		}

		private void _inputLine_SignalChange(object sender, SignalChangeEventArgs e)
		{
			UpdateSignal();
		}

		private void UpdateSignal()
		{
			if (_inputLineA == null || _inputLineB == null)
				return;

			SetSignal(_operation(_inputLineA.Signal, _inputLineB.Signal));
		}
	}
}
